// sgu354
// run: time -p $exec < input > out
// opt: 2
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <limits>
#include <ctime>
#include <vector>
#include <queue>

#define DEBUG 0

void scan(int & a)
{
	a = 0;
	bool flag = false;
	char ch = std::getchar();
	if (ch == '-')
		flag = true;
	else if (ch >= '0' && ch <= '9')
		a = ch - '0';

	while ((ch = std::getchar()) >= '0' && ch <= '9')
		a = a * 10 + ch - '0';
	if (flag) a = -a;
}

int const maxn = 700;
int rand_num[maxn * 10];
int rand_alloc = 0;

void init_rand()
{
	int len = maxn * maxn;
	for (int i = 0; i < 10 * maxn; i++)
		rand_num[i] = rand() % len;
}

namespace treap
{
	int big_rand()
	{
		rand_alloc = (rand_alloc + 1) % (10 * maxn);
		return rand_num[rand_alloc];
	}

	int const int_max = std::numeric_limits<int>::max();
	int const maxsize = 10000;

	struct node
	{
		node() { size = 0; key = int_max; }
		node(int value);
		void update() { size = child[0]->size + child[1]->size + 1; }

		int key, value, size;
		node * child[2];
	} * null = new node, memory[maxsize], * alloc = memory;

	node::node(int value) : value(value)
	{
		size = 1;
		child[0] = child[1] = null;
		key = big_rand();
	}

	// treap part
	node * root;

	node * alloc_node(int v)
	{
		alloc->value = v;
		alloc->size = 1;
		alloc->child[0] = alloc->child[1] = null;
		alloc->key = big_rand();
		return alloc++;
	}

	void rot(node *& t, bool d)
	{
		node* p = t->child[d];
		t->child[d] = p->child[!d];
		p->child[!d] = t;
		t->update();
		p->update();
		if (root == t) root = p;
		t = p;
	}

	void insert(node *& t, int x)
	{
		if (t == null) { t = alloc_node(x); return; }
		bool dir = x > t->value;
		insert(t->child[dir], x);
		if (t->child[dir]->key < t->key) rot(t, dir);
		else t->update();
	}

	void init()
	{
		root = null;
		alloc = memory;
		insert(root, -int_max);
		insert(root, +int_max);
	}

	void insert_right_child_left_most_son(node *& t, int v, bool dir = 1)
	{
		if (t == null) { t = alloc_node(v); return; }
		insert_right_child_left_most_son(t->child[dir], v, 0);
		if (t->child[dir]->key < t->key) rot(t, dir);
		else t->update();
	}

	void kth_and_insert_succ(node *& t, int kth, int v)
	{
		int count = t->child[0]->size;
		if (kth == count) {
			insert_right_child_left_most_son(t, v);
			return;
		} else if (kth > count) {
			kth -= count + 1;
			kth_and_insert_succ(t->child[1], kth, v);
			if (t->child[1]->key < t->key) rot(t, 1);
			else t->update();
		} else {
			kth_and_insert_succ(t->child[0], kth, v);
			if (t->child[0]->key < t->key) rot(t, 0);
			else t->update();
		}
	}

	void insert(int p, int v)
	{
		kth_and_insert_succ(root, p, v);
	}

	void traverse(int a[], int & len, node * t = root)
	{
		if (t == null) return;
		traverse(a, len, t->child[0]);
		a[len++] = t->value;
		traverse(a, len, t->child[1]);
	}
}

struct data { int id, p; };
bool operator<(data const& a, data const& b)
{
	return a.p > b.p;
}

int top[maxn][maxn];
int left[maxn][maxn];
int n;

int col[maxn][maxn];
int row[maxn][maxn];

bool fill_col()
{
	for (int j = 1; j <= n; j++) {
		treap::init();
		for (int i = 1; i <= n; i++) {
			if (top[i][j] >= i) return false;
			treap::insert(i - 1 - top[i][j], (i - 1) * n + j);
		}
		int len = 0;
		treap::traverse(col[j], len);
	}
	return true;
}

bool fill_row()
{
	for (int i = 1; i <= n; i++) {
		treap::init();
		for (int j = 1; j <= n; j++) {
			if (left[i][j] >= j) return false;
			treap::insert(j - 1 - left[i][j], (i - 1) * n + j);
		}
		int len = 0;
		treap::traverse(row[i], len);
	}
	return true;
}

bool vis[maxn * maxn];
int ans[maxn * maxn];
std::vector<int> g[maxn * maxn];
int degree[maxn * maxn];

void add_edge(int u, int v)
{
	g[u].push_back(v);
	degree[v]++;
}

void work()
{
	if (!fill_col()) { std::printf("0\n"); return; }
	if (!fill_row()) { std::printf("0\n"); return; }

#if DEBUG
	std::cerr << "col:\n";
	for (int j = 1; j <= n; j++) {
		for (int i = 0; i <= n + 1; i++) std::cerr << col[j][i] << " ";
		std::cerr << "\n";
	}
	std::cerr << "\n";

	std::cerr << "row:\n";
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= n; j++) std::cerr << row[i][j] << " ";
		std::cerr << "\n";
	}
	std::cerr << "\n";
#endif


	for (int i = 1; i <= n; i++) {
		for (int j = 2; j <= n; j++)
			add_edge(col[i][j - 1], col[i][j]);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 2; j <= n; j++)
			add_edge(row[i][j - 1], row[i][j]);
	}

	std::queue<int> q;
	for (int i = 1; i <= n * n; i++)
		if (!degree[i]) {
			vis[i] = true;
			q.push(i);
		}

	int len = 1;
	while (!q.empty()) {
		int now = q.front(); q.pop();
		ans[now] = len++;
		for (auto v : g[now]) {
			if (vis[v]) continue;
			degree[v]--;
			if (!degree[v]) {
				q.push(v); vis[v] = true;
			}
		}
	}

	if (len <= n * n) { std::printf("0\n"); return; }
	for (int i = 1; i <= n; i++) {
		std::printf("%d", ans[(i - 1) * n + 1]);
		for (int j = 2; j <= n; j++)
			std::printf(" %d", ans[(i - 1) * n + j]);
		std::printf("\n");
	}
}

int main()
{
	std::srand(std::time(0));
	init_rand();
	scan(n);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) scan(top[i][j]);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) scan(left[i][j]);

	work();
}

